 /*******************************************************************************
  * Copyright (c) 2005, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.cheatsheets.composite.views;

 import java.util.HashMap ;
 import java.util.Map ;
 import java.util.Observable ;
 import java.util.Observer ;

 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.SashForm;
 import org.eclipse.swt.events.ControlAdapter;
 import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.MenuItem;
 import org.eclipse.ui.forms.FormColors;
 import org.eclipse.ui.forms.HyperlinkSettings;
 import org.eclipse.ui.forms.IFormColors;
 import org.eclipse.ui.forms.ManagedForm;
 import org.eclipse.ui.forms.events.HyperlinkAdapter;
 import org.eclipse.ui.forms.events.HyperlinkEvent;
 import org.eclipse.ui.forms.widgets.FormText;
 import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin;
 import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource;
 import org.eclipse.ui.internal.cheatsheets.Messages;
 import org.eclipse.ui.internal.cheatsheets.actions.IMenuContributor;
 import org.eclipse.ui.internal.cheatsheets.composite.explorer.RestartAllAction;
 import org.eclipse.ui.internal.cheatsheets.composite.explorer.TreeExplorerMenu;
 import org.eclipse.ui.internal.cheatsheets.composite.model.AbstractTask;
 import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel;
 import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetSaveHelper;
 import org.eclipse.ui.internal.cheatsheets.composite.model.EditableTask;
 import org.eclipse.ui.internal.cheatsheets.composite.parser.ICompositeCheatsheetTags;
 import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader;
 import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader.TaskExplorerNode;
 import org.eclipse.ui.internal.cheatsheets.state.ICheatSheetStateManager;
 import org.eclipse.ui.internal.cheatsheets.views.Page;
 import org.eclipse.ui.internal.cheatsheets.views.ViewUtilities;
 import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask;
 import org.eclipse.ui.internal.provisional.cheatsheets.TaskEditor;
 import org.eclipse.ui.internal.provisional.cheatsheets.TaskExplorer;
 import org.eclipse.ui.part.PageBook;

 /**
  * A page which represents a composite cheat sheet
  */

 public class CompositeCheatSheetPage extends Page implements ISelectionChangedListener, IMenuContributor {

     public static final String REVIEW_TAG = "__review__"; //$NON-NLS-1$
 public static final String END_REVIEW_TAG = "__endReview__"; //$NON-NLS-1$
 public static final String GOTO_TASK_TAG = "__goto__"; //$NON-NLS-1$
 public static final String START_HREF = "__start__"; //$NON-NLS-1$
 public static final String SKIP_HREF = "__skip__"; //$NON-NLS-1$

     private ManagedForm mform;
     private PageBook explorerContainer;
     private PageBook taskEditorContainer;
     private CompositeCheatSheetModel model;
     private TaskExplorer currentExplorer;
     private DescriptionPanel descriptionPanel;
     
     private CompositeCheatSheetSaveHelper saveHelper;
     
     private ICompositeCheatSheetTask selectedTask;
     private boolean initialized = false;;
     
     public CompositeCheatSheetPage(CompositeCheatSheetModel model, ICheatSheetStateManager stateManager) {
         this.model = model;
         saveHelper = new CompositeCheatSheetSaveHelper(stateManager);
     }

     public void createPart(Composite parent) {
         init(parent.getDisplay());
         toolkit.getHyperlinkGroup().setHyperlinkUnderlineMode(HyperlinkSettings.UNDERLINE_HOVER);
         form = toolkit.createScrolledForm(parent);
         form.setLayoutData(new GridData(GridData.FILL_BOTH));
         FormColors colors = toolkit.getColors();
         /*
         colors.initializeSectionToolBarColors();
         Color gbg = colors.getColor(FormColors.TB_GBG);
         Color bg = colors.getBackground();
         form.getForm().setTextBackground(new Color[]{bg, gbg}, new int [] {100}, true);
         form.getForm().setSeparatorColor(colors.getColor(FormColors.TB_BORDER));
         form.getForm().setSeparatorVisible(true);
         */
         toolkit.decorateFormHeading(form.getForm());
         mform = new ManagedForm(toolkit, form);
         GridLayout glayout = new GridLayout();
         glayout.marginHeight = 0;
         glayout.marginWidth = 0;
         form.getBody().setLayout(glayout);
         final SashForm sash = new SashForm(form.getBody(), SWT.NULL);
         GridData gd = new GridData(GridData.FILL_BOTH);
         gd.widthHint = 10;
         gd.heightHint = 10;
         sash.setLayoutData(gd);
         sash.addControlListener(new ControlAdapter() {
             public void controlResized(ControlEvent e) {
                 Point size = sash.getSize();
                 if (size.x>size.y)
                     sash.setOrientation(SWT.HORIZONTAL);
                 else
                     sash.setOrientation(SWT.VERTICAL);
                 updateSashPanelMargins(sash);
             }
         });
         sash.setBackground(colors.getColor(IFormColors.TB_BG));
         
         Composite explorerPanel = new Composite(sash, SWT.NULL);
         explorerPanel.setBackground(colors.getColor(IFormColors.TB_BORDER));
         GridLayout playout = new GridLayout();
         playout.marginWidth = 0;
         playout.marginTop = 2;
         playout.marginBottom = 0;
         explorerPanel.setLayout(playout);
         toolkit.adapt(explorerPanel);
         explorerContainer = new PageBook(explorerPanel, SWT.NULL);
         explorerContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
         Composite editorPanel = new Composite(sash, SWT.NULL);
         playout = new GridLayout();
         playout.marginWidth = 0;
         playout.marginHeight = 0;
         editorPanel.setLayout(playout);
         editorPanel.setBackground(colors.getColor(IFormColors.TB_BORDER));
         taskEditorContainer = new PageBook(editorPanel, SWT.NULL);
         toolkit.adapt(taskEditorContainer);
         taskEditorContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
     }
     
     private void updateSashPanelMargins(SashForm sash) {
         Control [] children = sash.getChildren();
         int orientation = sash.getOrientation();
         // update task explorer panel
 GridLayout layout = (GridLayout)((Composite)children[0]).getLayout();
         if (orientation==SWT.HORIZONTAL) {
             layout.marginBottom = 0;
             layout.marginRight = 1;
         }
         else {
             layout.marginBottom = 1;
             layout.marginRight = 0;
         }
         // update task editor panel
 layout = (GridLayout)((Composite)children[1]).getLayout();
         if (orientation==SWT.HORIZONTAL) {
             layout.marginTop = 0;
             layout.marginLeft = 1;
         }
         else {
             layout.marginTop = 1;
             layout.marginLeft = 0;
         }
         ((Composite)children[0]).layout();
         ((Composite)children[1]).layout();
     }

     public void dispose() {
         mform.dispose();
         super.dispose();
     }

     private void setInputModel(CompositeCheatSheetModel model, Map layout) {
         this.model = model;
         mform.getForm().setText(ViewUtilities.escapeForLabel(model.getName()));
         String explorerId = model.getTaskExplorerId();
         setCurrentExplorerFromId(explorerId);
         String selectedTaskId = (String ) layout.get(ICompositeCheatsheetTags.SELECTED_TASK);
         ICompositeCheatSheetTask selectedTask= null;
         if (selectedTaskId != null) {
             selectedTask = model.getDependencies().getTask(selectedTaskId);
             if (selectedTask != null) {
                 currentExplorer.setSelection(new StructuredSelection(selectedTask), true);
             }
         }
         if (selectedTask != null) {
             updateSelectedTask(selectedTask);
         } else {
             updateSelectedTask(model.getRootTask());
         }
         model.addObserver(new Observer () {
             public void update(Observable o, Object arg) {
                 ICompositeCheatSheetTask task = (ICompositeCheatSheetTask)arg;
                 if (currentExplorer!=null)
                     currentExplorer.taskUpdated(task);
                 updateTask(task);
             }
         });
     }

     private void setCurrentExplorerFromId(String explorerId) {
         TaskExplorer explorer = getTaskExplorer(explorerId);
         if (explorer!=null) {
             explorer.setCompositeCheatSheet(this.model);
             explorer.setFocus();
             setExplorerVisible(explorerId);
         }
     }

     private void setExplorerVisible(String id) {
         Control [] excontrols = explorerContainer.getChildren();
         TaskExplorer explorer=null;
         for (int i=0; i<excontrols.length; i++) {
             Control excontrol = excontrols[i];
             explorer = (TaskExplorer)excontrol.getData(ICompositeCheatsheetTags.EXPLORER);
             if (explorer.getId().equals(id)) {
                 explorerContainer.showPage(excontrol);
                 setCurrentExplorer(explorer);
                 return;
             }
         }
     }
     
     private void setCurrentExplorer(TaskExplorer explorer) {
         if (currentExplorer!=null) {
             currentExplorer.getSelectionProvider().removeSelectionChangedListener(this);
         }
         currentExplorer = explorer;
         currentExplorer.getSelectionProvider().addSelectionChangedListener(this);
         updateForSelection(currentExplorer.getSelectionProvider().getSelection());
     }

     private TaskExplorer getTaskExplorer(String id) {
         Control [] excontrols = explorerContainer.getChildren();
         TaskExplorer explorer=null;
         for (int i=0; i<excontrols.length; i++) {
             Control excontrol = excontrols[i];
             Object data = excontrol.getData(ICompositeCheatsheetTags.EXPLORER);
             if (data instanceof TaskExplorer) {
                 explorer = (TaskExplorer)data;
                 if (explorer.getId().equals(id)) {
                     return explorer;
                 }
             }
         }
     
         // Load the explorer from an extension point

         TaskExplorerManager explorerManager = TaskExplorerManager.getInstance();
         explorer = explorerManager.getExplorer(id);
         if (explorer != null) {
             explorer.createControl(explorerContainer, mform.getToolkit());
             explorer.getControl().setData(ICompositeCheatsheetTags.EXPLORER, explorer);
             new TreeExplorerMenu(explorer);
         }
         return explorer;
     }

     public void selectionChanged(SelectionChangedEvent event) {
         updateForSelection(event.getSelection());
     }

     private void updateForSelection(ISelection selection) {
         Object selectedElement = ((IStructuredSelection)selection).getFirstElement();
         if (selectedElement instanceof ICompositeCheatSheetTask) {
             updateSelectedTask((ICompositeCheatSheetTask)selectedElement);
         }
     }
     
     private void updateSelectedTask(ICompositeCheatSheetTask task) {
         selectedTask = task;
         updateTask(selectedTask);
     }

     /*
      * Update can be called as a result of a selection change or a state change
      * If this is not the selected task wait till it is selected to display it
      */
     private void updateTask(ICompositeCheatSheetTask task) {
         if (task==null || task != selectedTask) return;
         if ( task instanceof EditableTask) {
             EditableTask editable = (EditableTask)task;
             if (editable.getState() == ICompositeCheatSheetTask.IN_PROGRESS) {
                 showEditor(editable);
                 return;
             } else if (editable.isUnderReview()) {
                 if (editable.getState() == ICompositeCheatSheetTask.COMPLETED) {
                     showEditor(editable);
                 } else {
                     endReview(editable);
                 }
                 return;
             }
         }
         showDescription(task);
     }

     public void saveState() {
         Map layout = new HashMap ();
         if (selectedTask != null) {
             layout.put(ICompositeCheatsheetTags.SELECTED_TASK, selectedTask.getId());
         }
         saveHelper.saveCompositeState(model, layout);
     }

     private void showDescription(final ICompositeCheatSheetTask task) {
         if (descriptionPanel==null) {
             createDescriptionPanel();
         }
         descriptionPanel.showDescription(task);
         setCurrentEditor(descriptionPanel.getControl());
     }

     private void createDescriptionPanel() {
         descriptionPanel = new DescriptionPanel(mform, taskEditorContainer);
         descriptionPanel.addHyperlinkListener(new DescriptionLinkListener());
     }

     /*
      * Ensure that if this task is visible and in a runnable state that it has been started
      */
     private void showEditor(EditableTask task) {
         if (task == selectedTask) {
             TaskEditor editor = getTaskEditor(task);
             if (editor!= null) {
                 if (!task.isEditorInitialized()) {
                     task.setInput(model.getTaskMemento(task.getId()));
                 }
                 setCurrentEditor(editor.getControl().getParent());
             }
         }
     }

     private void addHyperlink(StringBuffer buf, String href, String imageRef, String message) {
         buf.append("<p><a HREF=\""); //$NON-NLS-1$
 buf.append(href);
         buf.append("\">"); //$NON-NLS-1$
 buf.append("<img HREF=\""); //$NON-NLS-1$
 buf.append(imageRef);
         buf.append("\"/> "); //$NON-NLS-1$
 buf.append(message);
         buf.append("</a></p>"); //$NON-NLS-1$
 }

     private void reviewTask(EditableTask task) {
         TaskEditor taskEditor = getTaskEditor(task);
         Composite container = taskEditor.getControl().getParent();
         Composite separator = toolkit.createCompositeSeparator(container);
         GridData data = new GridData();
         data.heightHint = 1;
         data.horizontalAlignment = SWT.FILL;
         separator.setLayoutData(data);
         FormText text = toolkit.createFormText(container, false);
         text.setImage(DescriptionPanel.REVIEW_IMAGE, CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.COMPOSITE_TASK_REVIEW));
         text.addHyperlinkListener(getEndReviewListener());
         text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
         StringBuffer buf = new StringBuffer ();
         buf.append("<form>"); //$NON-NLS-1$
 addHyperlink(buf, END_REVIEW_TAG + task.getId(), DescriptionPanel.REVIEW_IMAGE, Messages.COMPOSITE_PAGE_END_REVIEW );
         buf.append("</form>"); //$NON-NLS-1$
 text.setText(buf.toString(), true, false);
         task.setUnderReview(true);
         container.layout(true);
         showEditor(task);
     }
     
     private void endReview(EditableTask task) {
         TaskEditor taskEditor = getTaskEditor(task);
         Control editorControl = taskEditor.getControl();
         Composite container = editorControl.getParent();
         Control[] children = container.getChildren();
         for (int i = children.length -2; i < children.length; i++) {
             children[i].dispose();
         }
         task.setUnderReview(false);
         showDescription(task);
         container.layout();
     }
     
     private void setCurrentEditor(Control c) {
         taskEditorContainer.showPage(c);
     }
     
     /**
      * Class which responds to hyperlink events originating from the
      * description panel.
      */
     private final class DescriptionLinkListener extends HyperlinkAdapter {
         public void linkActivated(HyperlinkEvent e) {
             String ref = (String )e.getHref();
             if (ref.equals(START_HREF)) {
                 Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK);
                 if (data instanceof EditableTask) {
                     EditableTask task = (EditableTask)data;
                     task.setStarted();
                 }
             }
             if (ref.equals(SKIP_HREF)) {
                 Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK);
                 if (data instanceof AbstractTask) {
                     AbstractTask task = (AbstractTask)data;
                     task.setState(ICompositeCheatSheetTask.SKIPPED);
                 }
             }
             if (ref.equals(REVIEW_TAG)) {
                 Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK);
                 if (data instanceof EditableTask) {
                     reviewTask((EditableTask) data);
                 }
             }
             if (ref.startsWith(GOTO_TASK_TAG)) {
                 String next = ref.substring(GOTO_TASK_TAG.length());
                 AbstractTask nextTask =
                     model.getDependencies().getTask(next);
                 currentExplorer.setSelection
                     (new StructuredSelection(nextTask), true);
             }
         }
     }
     
     /**
      * Class which responds to hyperlink events originating from the
      * end review panel
      */
     private final class EndReviewListener extends HyperlinkAdapter {
         public void linkActivated(HyperlinkEvent e) {
             String ref = (String )e.getHref();
             if (ref.startsWith(END_REVIEW_TAG)) {
                 String next = ref.substring(END_REVIEW_TAG.length());
                 AbstractTask task =
                     model.getDependencies().getTask(next);
                 endReview((EditableTask)task);
             }
         }
     }
     
     private EndReviewListener endReviewListener;
     
     private EndReviewListener getEndReviewListener() {
         if (endReviewListener == null) {
             endReviewListener = new EndReviewListener();
         }
         return endReviewListener;
     }


     /*
      * Get the task editor for this task. If no editor exists create one
      */
     private TaskEditor getTaskEditor(EditableTask editable) {
         if (editable.getEditor() == null) {
             // Create a new editor using the extension point data
 TaskEditor editor = TaskEditorManager.getInstance().getEditor(editable.getKind());
             if (editor != null) {
                 Composite editorPanel = new Composite(taskEditorContainer, SWT.NULL);
                 GridLayout layout = new GridLayout();
                 layout.marginWidth = 0;
                 layout.marginHeight = 0;
                 editorPanel.setLayout(layout);
                 mform.getToolkit().adapt(editorPanel);
                 editor.createControl(editorPanel, mform.getToolkit());
                 editable.setEditor(editor);
                 GridData gridData = new GridData(GridData.FILL_BOTH);
                 editor.getControl().setLayoutData(gridData);
             }
         }
         return editable.getEditor();
     }

     public Control getControl() {
         return form;
     }

     protected String getTitle() {
         return model.getName();
     }

     public void initialized() {
         // Open the model
 model.setSaveHelper(saveHelper);
         Map layout = new HashMap ();
         model.loadState(layout);
         setInputModel(model, layout);
         initialized = true;
     }

     public int contributeToViewMenu(Menu menu, int index) {
         if (!initialized) {
             return index;
         }
         index = contributeExplorerMenuItem(menu, index);
         return contributeRestartMenuItem(menu, index);
     }

     private int contributeRestartMenuItem(Menu menu, int index) {
         MenuItem item = new MenuItem(menu, SWT.PUSH, index++);
         item.setText(Messages.RESTART_ALL_MENU);
         item.setImage(CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.COMPOSITE_RESTART_ALL));
         item.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent e) {
                 if (model != null) {
                     if (RestartAllAction.confirmRestart()) {
                         restart(null);
                     }
                 }
             }
         });
         return index;
     }

     private int contributeExplorerMenuItem(Menu menu, int index) {
         String [] explorerIds = CheatSheetRegistryReader.getInstance().getExplorerIds();
         if (explorerIds.length == 1) {
             return index; // no other explorer to chosse from
 }
         MenuItem menuItem = new MenuItem(menu, SWT.CASCADE, index++);

         menuItem.setText(Messages.EXPLORER_PULLDOWN_MENU);
     
         Menu subMenu = new Menu(menu);
         menuItem.setMenu(subMenu);
         
         for (int i = 0; i < explorerIds.length; i++) {
             final String id = explorerIds[i];
             TaskExplorerNode node = CheatSheetRegistryReader.getInstance().findTaskExplorer(id);
             boolean isCurrentExplorer = id.equals(currentExplorer.getId());
             int style = isCurrentExplorer ? SWT.RADIO: SWT.PUSH;
             MenuItem item = new MenuItem(subMenu, style);
             item.setText(node.getName());
             item.setSelection(isCurrentExplorer);
             item.setImage(TaskExplorerManager.getInstance().getImage(id));
             item.addSelectionListener(new SelectionAdapter() {
                 public void widgetSelected(SelectionEvent e) {
                     setCurrentExplorerFromId(id);
                 }
             });
         }

         return index;
     }

     public void restart(Map cheatSheetData) {
         model.resetAllTasks(cheatSheetData);
         currentExplorer.setSelection
             (new StructuredSelection(model.getRootTask()), true);
         
     }

 }

